package com.ai.bitcoin.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.ai.bitcoin.constant.CodeConstant;
import com.ai.bitcoin.constant.CommonConstant;
import com.ai.bitcoin.enums.RedeemCodeEnum;
import com.ai.bitcoin.enums.RedeemCodeStatusEnum;
import com.ai.bitcoin.exception.BusinessException;
import com.ai.bitcoin.input.RedeemImportExcelDto;
import com.ai.bitcoin.input.RedeemInputDto;
import com.ai.bitcoin.jpa.dao.*;
import com.ai.bitcoin.jpa.entity.*;
import com.ai.bitcoin.output.GeneralReponse;
import com.ai.bitcoin.service.RedeemCodeService;
import com.ai.bitcoin.service.helper.RedeemServiceHelper;
import com.ai.bitcoin.service.helper.UserServiceHelper;
import com.ai.bitcoin.util.DateUtil;
import com.ai.bitcoin.util.JacksonUtils;
import com.ai.bitcoin.weixin.template.BonusChangeTemplate;
import com.ai.bitcoin.weixin.template.CoinReportTemplate;
import com.ai.bitcoin.weixin.template.SendReportService;
import com.ai.bitcoin.weixin.template.TemplateManager;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.*;

@Service
@Slf4j
public class RedeemServiceImpl implements RedeemCodeService {

    private static final String CHARARTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private String PUSH_REPORT_PREFIX_URL = "https://www.valuechain.group/template/index.html?";

    @Autowired
    private RedeemCodeDao redeemCodeDao;

    @Autowired
    private RedeemCodeOperationDetailDao redeemCodeOperationDetailDao;

    @Autowired
    private BccSubCoinDao bccSubCoinDao;

    @Autowired
    private BccSubCoinActionDao bccSubCoinActionDao;

    @Autowired
    private BccBasicInfoDao bccBasicInfoDao;

    @Autowired
    private BccUserDao bccUserDao;

    @Autowired
    private BccUserPlcRecordDao bccUserPlcRecordDao;

    @Autowired
    private RedeemServiceHelper redeemServiceHelper;

    @Autowired
    private UserServiceHelper userServiceHelper;

    @Autowired
    private SendReportService sendReportService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public GeneralReponse create(RedeemInputDto dto) {
        Set<RedeemCode> list = new HashSet<>();
        //如果为积分类型
        if (RedeemCodeEnum.PLC.getCode() == dto.getType()) {
            for (int i = 0; i < dto.getNum(); i++) {
                StringBuilder code = new StringBuilder("");
                code.append("PLC");
                Map<String, Object> data = dto.getData();
                Integer plc = (Integer) data.get("plc");
                if (plc == null || plc <= 0) {
                    return new GeneralReponse(CodeConstant.PARAM_ERROR, "plc参数有误");
                }
                code.append(plc);
                code.append("-");
                code.append(getCode(6));
                RedeemCode redeemCode = new RedeemCode();
                redeemCode.setCreateTime(new Date());
                redeemCode.setUpdateTime(new Date());
                redeemCode.setCode(code.toString());
                redeemCode.setData(JacksonUtils.toJson(dto.getData()));
                redeemCode.setStatus(RedeemCodeStatusEnum.PRE_RECEIVE.getCode());
                redeemCode.setType(dto.getType());
                redeemCode.setRemark(dto.getRemark());
                redeemCode.setExpiredDate(DateUtil.appointDate("yyyy-MM-dd HH:mm:ss", dto.getExpiredDate()));
                list.add(redeemCode);
            }
        }
        else if (RedeemCodeEnum.ORDER.getCode() == dto.getType()) {//如果为订阅类型
            for (int i = 0; i < dto.getNum(); i++) {
                StringBuilder code = new StringBuilder("");
                code.append("DY");
                Map<String, Object> data = dto.getData();
                String bccId = (String) data.get("bccId");
                Integer month = (Integer) data.get("month");
                //根据bccid查询是否有该币
                BccBasicInfo bccInfo = bccBasicInfoDao.findByBccId(bccId);
                if (bccInfo == null) {
                    return new GeneralReponse(CodeConstant.PARAM_ERROR, "该币不存在！");
                }
                if (month == null || month <= 0) {
                    return new GeneralReponse(CodeConstant.PARAM_ERROR, "month参数有误");
                }
                code.append(bccId);
                code.append("T");
                code.append(month);
                code.append("-");
                code.append(getCode(6));
                RedeemCode redeemCode = new RedeemCode();
                redeemCode.setCreateTime(new Date());
                redeemCode.setUpdateTime(new Date());
                redeemCode.setCode(code.toString());
                redeemCode.setData(JacksonUtils.toJson(dto.getData()));
                redeemCode.setStatus(RedeemCodeStatusEnum.PRE_RECEIVE.getCode());
                redeemCode.setType(dto.getType());
                redeemCode.setRemark(dto.getRemark());
                redeemCode.setExpiredDate(DateUtil.appointDate("yyyy-MM-dd HH:mm:ss", dto.getExpiredDate()));
                list.add(redeemCode);
            }
        }
        else {
            return new GeneralReponse(CodeConstant.PARAM_ERROR, "兑换卷类型有误");
        }

        //批量存储(如果数据量大，则分批)
        List<RedeemCode> batchList = new ArrayList<>();

        int result = 0;
        for (RedeemCode redeemCode : list) {
            if (batchList.size() == 300) {
                List list1 = redeemCodeDao.saveAll(batchList);
                result += list1.size();
                batchList.clear();
            }

            batchList.add(redeemCode);
        }

        if (!batchList.isEmpty()) {
            List list1 = redeemCodeDao.saveAll(batchList);
            result += list1.size();
        }
        return new GeneralReponse(CodeConstant.SUCCESS, result);
    }

    /**
     * 兑换
     *
     * @param code
     * @return
     */
    @Override
    public GeneralReponse exchange(String code, BccUser bccUser) {
        RedeemCode redeemCode = redeemCodeDao.findByCode(code);
        if (redeemCode == null) {
            return new GeneralReponse(CodeConstant.PARAM_ERROR, null, "兑换码有误！");
        }
        if (RedeemCodeStatusEnum.EXPIRED.getCode() == redeemCode.getStatus() ||
                RedeemCodeStatusEnum.EXCHANGED.getCode() == redeemCode.getStatus()) {
            return new GeneralReponse(CodeConstant.CODE_HAS_BEEN_EXCHANGED, null, "兑换码已兑换或已过期！");
        }
        Map<String, Object> data = JacksonUtils.fromJson(redeemCode.getData(), Map.class);
        Date date = redeemCode.getExpiredDate();
        if (date == null) {
            return new GeneralReponse(CodeConstant.SERVER_ERROR, null, "兑换码异常！");
        }
        if (date.before(new Date())) {
            return new GeneralReponse(CodeConstant.CODE_HAS_BEEN_EXPIRED, null, "兑换码已过期！");
        }
        //校验通过，判断兑换码类型
        if (RedeemCodeEnum.PLC.getCode() == redeemCode.getType()) {

            int ret = redeemServiceHelper.updateStatusById(RedeemCodeStatusEnum.EXCHANGED.getCode(), redeemCode.getId());
            if (ret == 1) {
                //TODO 给兑换的用户增加对应的积分
                Double plc = Double.valueOf((Integer) data.get("plc"));
                userServiceHelper.updateUserBonus(new Date(), plc, bccUser.getOpenId());
                bccUser = bccUserDao.findByOpenId(bccUser.getOpenId());

                Map<String, Object> datazengsong = new HashMap<>();
                datazengsong.put("openId", bccUser.getOpenId());
                datazengsong.put("url", "http://explorelife.vip/bitcoin/bitcoin/media/" + bccUser.getOpenId() + ".png");
                datazengsong.put("first", "您的积分(PLC)变动如下");
                datazengsong.put("FieldName", "增加原因");
                datazengsong.put("Account", "兑换赠送");
                datazengsong.put("change", "增加");
                datazengsong.put("CreditChange", plc);
                datazengsong.put("CreditTotal", bccUser.getBonus());
//                            data.put("Remark","您可以在网站或服务号使用积分下单抵现，10积分=1元。");
                sendReportService.sendTemplateMsg(TemplateManager.buildTemplateParater(new BonusChangeTemplate(), datazengsong));


                //更新兑换码明细表
                BccUserPlcRecord bccUserPlcRecord = BccUserPlcRecord.builder()
                        .plc(plc)
                        .ceateTime(new Date())
                        .openId(bccUser.getOpenId())
                        .reason("兑换码兑换")
                        .type(6)
                        .build();
                bccUserPlcRecordDao.save(bccUserPlcRecord);
                //插入兑换码操作表
                RedeemCodeOperationDetail detail = new RedeemCodeOperationDetail();
                detail.setCode(redeemCode.getCode());
                detail.setCreateTime(new Date());
                detail.setUpdateTime(new Date());
                detail.setType(1);//操作类型为兑换
                detail.setUserId(bccUser.getOpenId());
                detail.setUserName(bccUser.getNickname());
                detail.setDetail("用户id：" + bccUser.getId() + ",兑换了该兑换码，获得" + data.get("plc") + "plc");
                redeemCodeOperationDetailDao.save(detail);
                Map<String, Object> result = new HashMap<>();
                result.put("plc", plc);
                result.put("balance", bccUser.getBonus());
                result.put("type", RedeemCodeEnum.PLC.getCode());
                return new GeneralReponse(CodeConstant.SUCCESS, result);
            }

            //更新兑换码状态(加锁)
//            redeemCode.setStatus(RedeemCodeStatusEnum.EXCHANGED.getCode());
//            int i = redeemCodeDao.updateStatusById(RedeemCodeStatusEnum.EXCHANGED.getCode(),redeemCode.getId());
//            if(i!=1){
//                throw new RuntimeException("兑换码异常！");
//            }
            else {
                return new GeneralReponse(CodeConstant.CODE_HAS_BEEN_EXCHANGED, null);
            }

        }
        else if (RedeemCodeEnum.ORDER.getCode() == redeemCode.getType()) {
            //TODO 给兑换的用户增加添加相应的订阅
            String bccId = (String) data.get("bccId");
            BccBasicInfo bccBasicInfo = bccBasicInfoDao.findByBccId(bccId);
            if (bccBasicInfo == null) {
                return new GeneralReponse(CodeConstant.PARAM_ERROR, null, "该币不存在！");
            }


            int ret = redeemServiceHelper.updateStatusById(RedeemCodeStatusEnum.EXCHANGED.getCode(), redeemCode.getId());
            if (ret == 1) {

                String bccName = bccBasicInfo.getNameEn();
                Map<String, Object> templeMap = new HashMap<String, Object>();
                templeMap.put("openId", bccUser.getOpenId());
                templeMap.put("url", PUSH_REPORT_PREFIX_URL + "bccId=" + bccId + "T" + DateUtil.currentDate2Str(CommonConstant.REPORT_HTML_PREFIX_PATH));
                templeMap.put("first", "您有一个新币(" + bccName + ")报告已生成，点击可查看报告内容。");
                templeMap.put("keyword1", bccName);
                templeMap.put("keyword2", bccName);
                templeMap.put("remark", "感谢您的使用");
                sendReportService.sendTemplateMsg(TemplateManager.buildTemplateParater(new CoinReportTemplate(), templeMap));

                BccSubCoin bccSubCoin = bccSubCoinDao.findByUserOpenIdAndBccId(bccUser.getOpenId(), bccId);
                //用户未订阅过，直接新增
                if (bccSubCoin == null) {
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(new Date());
                    calendar.add(Calendar.MONTH, (Integer) data.get("month"));//增加订阅的月数
                    BccSubCoin record = BccSubCoin.builder()
                            .bccId(bccId)
                            .bccName(StringUtils.isBlank(bccBasicInfo.getNameCn()) ? bccBasicInfo.getNameEn() : bccBasicInfo.getNameCn())
                            .createTime(new Date())
                            .endTime(calendar.getTimeInMillis() / 1000)
                            .status(1)
                            .updateTime(new Date())
                            .userOpenId(bccUser.getOpenId())
                            .build();
                    bccSubCoinDao.save(record);
                }
                else {
                    Date endTime = new Date(bccSubCoin.getEndTime() * 1000);
                    Calendar calendar = Calendar.getInstance();
                    //订阅已过期
                    if (endTime.before(new Date())) {
                        calendar.setTime(new Date());
                    }
                    else {
                        calendar.setTime(endTime);
                    }
                    calendar.add(Calendar.MONTH, (Integer) data.get("month"));//增加订阅的月数
                    bccSubCoin.setEndTime(calendar.getTimeInMillis() / 1000);
                    bccSubCoinDao.save(bccSubCoin);
                }
                //订阅明细表？
                BccSubCoinAction bccSubCoinAction = BccSubCoinAction.builder()
                        .bccId(bccId)
                        .bccName(StringUtils.isBlank(bccBasicInfo.getNameCn()) ? bccBasicInfo.getNameEn() : bccBasicInfo.getNameCn())
                        .createTime(new Date())
                        .type(2)
                        .validTime(Long.valueOf(String.valueOf(data.get("month"))).longValue() * 30 * 24 * 60 * 60)
                        .userOpenId(bccUser.getOpenId())
                        .build();
                bccSubCoinActionDao.save(bccSubCoinAction);
                //插入兑换码操作表
                RedeemCodeOperationDetail detail = new RedeemCodeOperationDetail();
                detail.setCode(redeemCode.getCode());
                detail.setCreateTime(new Date());
                detail.setUpdateTime(new Date());
                detail.setType(2);//操作类型为兑换
                detail.setUserId(bccUser.getOpenId());
                detail.setUserName(bccUser.getNickname());
                detail.setDetail("用户id：" + bccUser.getId() + ",兑换了该兑换码，获得" +
                        (StringUtils.isBlank(bccBasicInfo.getNameCn()) ? bccBasicInfo.getNameEn() : bccBasicInfo.getNameCn())
                        + "的" + data.get("month") + "个月的订阅服务");
                redeemCodeOperationDetailDao.save(detail);

                Map<String, Object> result = new HashMap<>(3, 1.0f);
                result.put("type", RedeemCodeEnum.ORDER.getCode());
                result.put("bccName", StringUtils.isBlank(bccBasicInfo.getNameCn()) ? bccBasicInfo.getNameEn() : bccBasicInfo.getNameCn());
                result.put("month", data.get("month"));
                return new GeneralReponse(CodeConstant.SUCCESS, result, "恭喜获得" +
                        (StringUtils.isBlank(bccBasicInfo.getNameCn()) ? bccBasicInfo.getNameEn() : bccBasicInfo.getNameCn())
                        + "的" + data.get("month") + "个月的订阅服务！");
            }
            else {
                return new GeneralReponse(CodeConstant.CODE_HAS_BEEN_EXCHANGED, null);
            }

            //更新兑换码状态(加锁)
//            redeemCode.setStatus(RedeemCodeStatusEnum.EXCHANGED.getCode());
//            int i = redeemCodeDao.updateStatusById(RedeemCodeStatusEnum.EXCHANGED.getCode(),redeemCode.getId());
//            if(i!=1){
//                throw new RuntimeException("兑换码异常！");
//            }

        }
        else {
            return new GeneralReponse(CodeConstant.SERVER_ERROR, null, "无法识别的兑换码类型！");
        }
    }


    /**
     * 随机产生length位长度的字符串，每4位以空格分隔
     *
     * @param length 兑换码长度
     * @return
     */
    public static String getCode(int length) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < length; i++) {
            Random random = new Random();
            int index = random.nextInt(36);
            stringBuilder.append(CHARARTERS.charAt(index));
        }
        return stringBuilder.toString().trim();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity<byte[]> createByExcel(InputStream is) throws Exception {
        List<RedeemImportExcelDto> dtoList = ExcelImportUtil.importExcel(is, RedeemImportExcelDto.class, new ImportParams());
        List<RedeemCode> list = Lists.newArrayList();
        for (RedeemImportExcelDto dto : dtoList) {
            //如果为积分类型
            if (RedeemCodeEnum.PLC.getCode() == dto.getCodeType()) {
                StringBuilder code = new StringBuilder("");
                code.append("PLC");
                Map<String, Object> data = Maps.newHashMap();
                data.put("plc", dto.getPlcCount());
                code.append(dto.getPlcCount());
                code.append("-");
                code.append(getCode(6));
                RedeemCode redeemCode = new RedeemCode();
                redeemCode.setCreateTime(new Date());
                redeemCode.setUpdateTime(new Date());
                redeemCode.setCode(code.toString());
                dto.setCode(redeemCode.getCode());
                redeemCode.setData(JacksonUtils.toJson(data));
                redeemCode.setStatus(RedeemCodeStatusEnum.PRE_RECEIVE.getCode());
                redeemCode.setType(dto.getCodeType());
                redeemCode.setRemark(dto.getRemark());
                try {
                    redeemCode.setExpiredDate(DateUtils.parseDate(dto.getExpiredDate(), "yyyyMMdd", "yyyy-MM-dd", "yyyy/MM/dd"));
                } catch (ParseException e) {
                    throw new BusinessException(CodeConstant.PARAM_ERROR, "有效期格式有误：" + dto.getExpiredDate());
                }
                list.add(redeemCode);
            }
            else if (RedeemCodeEnum.ORDER.getCode() == dto.getCodeType()) {//如果为订阅类型
                StringBuilder code = new StringBuilder("");
                code.append("DY");
                Map<String, Object> data = Maps.newHashMap();
                BccBasicInfo bcc = bccBasicInfoDao.findBySymbol(dto.getSymbol());
                if (bcc == null) {
                    throw new BusinessException(CodeConstant.PARAM_ERROR, "请输入正确的BCC标识：" + dto.getSymbol());
                }
                data.put("bccId", bcc.getBccId());
                data.put("month", dto.getMonthCount());
                String bccId = (String) data.get("bccId");
                Integer month = (Integer) data.get("month");
                code.append(bccId);
                code.append("T");
                code.append(month);
                code.append("-");
                code.append(getCode(6));
                RedeemCode redeemCode = new RedeemCode();
                redeemCode.setCreateTime(new Date());
                redeemCode.setUpdateTime(new Date());
                redeemCode.setCode(code.toString());
                dto.setCode(redeemCode.getCode());
                redeemCode.setData(JacksonUtils.toJson(data));
                redeemCode.setStatus(RedeemCodeStatusEnum.PRE_RECEIVE.getCode());
                redeemCode.setType(dto.getCodeType());
                redeemCode.setRemark(dto.getRemark());
                if (StringUtils.isNotEmpty(dto.getExpiredDate())) {
                    try {
                        redeemCode.setExpiredDate(DateUtils.parseDate(dto.getExpiredDate(), "yyyyMMdd", "yyyy-MM-dd", "yyyy/MM/dd"));
                    } catch (ParseException e) {
                        throw new BusinessException(CodeConstant.PARAM_ERROR, "有效期格式有误：" + dto.getExpiredDate());
                    }
                }

                list.add(redeemCode);
            }
            else {
                throw new BusinessException(CodeConstant.PARAM_ERROR, "兑换券类型有误");
            }
        }

        list = redeemCodeDao.saveAll(list);


        Workbook book = ExcelExportUtil.exportExcel(new ExportParams(),
                RedeemImportExcelDto.class, dtoList);

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        book.write(os);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", new String("兑换码".getBytes(StandardCharsets.UTF_8), "ISO8859-1") + ".xls");
        return new ResponseEntity<>(os.toByteArray(), headers, HttpStatus.CREATED);
    }

    public static void main(String[] args) throws IOException {
        ImportParams params = new ImportParams();
        List<RedeemImportExcelDto> result = ExcelImportUtil.importExcel(
                new File("C:\\Users\\lifei\\Documents\\WeChat Files\\lifeiquna\\Files\\兑换码生成v1.xlsx"),
                RedeemImportExcelDto.class, params);

        Workbook book = ExcelExportUtil.exportExcel(new ExportParams(),
                RedeemImportExcelDto.class, result);
        FileOutputStream fos = new FileOutputStream("C:\\Users\\lifei\\Documents\\WeChat Files\\lifeiquna\\Files\\export兑换码生成v1.xlsx");
        book.write(fos);
        fos.close();
    }
}
